﻿/* 
 PureMVC C# Multi-Core Port by Tang Khai Phuong <phuong.tang@puremvc.org>, et al.
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
*/

#region Using

using System;
using System.Collections.Generic;
using PureMVC.Core;
using PureMVC.Interfaces;
//using System.Collections.Concurrent;
//using Dictionary = System.Collections.Generic.Dictionary;

#endregion

namespace PureMVC.Patterns
{
    /// <see cref="PureMVC.Core.Model"/>
    /// <see cref="PureMVC.Core.View"/>
    /// <see cref="PureMVC.Core.Controller"/>
    /// <see cref="PureMVC.Patterns.Notification"/>
    /// <see cref="PureMVC.Patterns.Mediator"/>
    /// <see cref="PureMVC.Patterns.Proxy"/>
    /// <see cref="PureMVC.Patterns.SimpleCommand"/>
    /// <see cref="PureMVC.Patterns.MacroCommand"/>
    public class Facade : Notifier, IFacade
    {
        #region Constructors
        public Facade(string key)
        {
            if (m_instanceMap.ContainsKey(key))
                throw new Exception(MULTITON_MSG);
            InitializeNotifier(key);
            m_instanceMap[key] = this;
            InitializeFacade();
        }
        public Facade()
            : this(DEFAULT_KEY)
        { }

        #endregion

        #region Public Methods

        #region IFacade Members

        #region Proxy
        public void RegisterProxy(IProxy proxy)
        {
            m_model.RegisterProxy(proxy);
        }
        public IProxy RetrieveProxy(string proxyName)
        {
            return m_model.RetrieveProxy(proxyName);
        }
        public IProxy RemoveProxy(string proxyName)
        {
            return m_model.RemoveProxy(proxyName);
        }
        public bool HasProxy(string proxyName)
        {
            return m_model.HasProxy(proxyName);
        }

        #endregion

        #region Command
        public void RegisterCommand(string notificationName, Type commandType)
        {
            m_controller.RegisterCommand(notificationName, commandType);
        }
        public void RegisterCommand(string notificationName, ICommand command)
        {
            m_controller.RegisterCommand(notificationName, command);
        }
        public object RemoveCommand(string notificationName)
        {
            return m_controller.RemoveCommand(notificationName);
        }
        public bool HasCommand(string notificationName)
        {
            return m_controller.HasCommand(notificationName);
        }

        #endregion

        #region Mediator
        public void RegisterMediator(IMediator mediator)
        {
            m_view.RegisterMediator(mediator);
        }
        public IMediator RetrieveMediator(string mediatorName)
        {
            return m_view.RetrieveMediator(mediatorName);
        }
        public IMediator RemoveMediator(string mediatorName)
        {
            return m_view.RemoveMediator(mediatorName);
        }
        public bool HasMediator(string mediatorName)
        {
            return m_view.HasMediator(mediatorName);
        }

        #endregion

        #region Observer
        public void NotifyObservers(INotification notification)
        {
            m_view.NotifyObservers(notification);
        }

        #endregion

        #endregion

        #region INotifier Members
        public override void SendNotification(string notificationName)
        {
            NotifyObservers(new Notification(notificationName));
        }
        public override void SendNotification(string notificationName, object body)
        {
            NotifyObservers(new Notification(notificationName, body));
        }
        public override void SendNotification(string notificationName, object body, string type)
        {
            NotifyObservers(new Notification(notificationName, body, type));
        }

        #endregion

        #endregion

        #region Accessors
        public static IFacade Instance
        {
            get { return GetInstance(DEFAULT_KEY); }
        }
        public static IFacade GetInstance()
        {
            return GetInstance(DEFAULT_KEY);
        }
        public static IFacade GetInstance(string key)
        {
            IFacade result;
            if (m_instanceMap.TryGetValue(key, out result))
                return result;

            result = new Facade(key);
            m_instanceMap[key] = result;
            return result;
        }
        public static bool HasCore(string key)
        {
            return m_instanceMap.ContainsKey(key);
        }
        public static IEnumerable<string> ListCore
        {
            get { return m_instanceMap.Keys; }
        }
        public static void RemoveCore(string key)
        {
            IFacade facade;
            if (!m_instanceMap.TryGetValue(key, out facade))
                return;

            m_instanceMap.Remove(key);
            facade.Dispose();

            Model.RemoveModel(key);
            Controller.RemoveController(key);
            View.RemoveView(key);
        }


        /// <summary>
        /// Release and dispose resource of facade.
        /// </summary>
        public void Dispose()
        {
            m_view = null;
            m_model = null;
            m_controller = null;
            m_instanceMap.Remove(MultitonKey);
        }
        public static void BroadcastNotification(INotification notification)
        {
            foreach (var facade in m_instanceMap)
                facade.Value.NotifyObservers(notification);
        }
        public static void BroadcastNotification(string notificationName)
        {
            BroadcastNotification(new Notification(notificationName));
        }
        public static void BroadcastNotification(string notificationName, object body)
        {
            BroadcastNotification(new Notification(notificationName, body));
        }
        public static void BroadcastNotification(string notificationName, object body, string type)
        {
            BroadcastNotification(new Notification(notificationName, body, type));
        }

        #endregion

        #region Protected & Internal Methods

        /// <summary>
        /// Explicit static constructor to tell C# compiler 
        /// not to mark type as before field initiate
        ///</summary>
        static Facade()
        {
            m_instanceMap = new Dictionary<string, IFacade>();
        }

        /// <summary>
        /// Initialize the Singleton <c>Facade</c> instance
        /// </summary>
        /// <remarks>
        /// <para>Called automatically by the constructor. Override in your subclass to do any subclass specific initializations. Be sure to call <c>base.initializeFacade()</c>, though</para>
        /// </remarks>
        protected virtual void InitializeFacade()
        {
            InitializeModel();
            InitializeController();
            InitializeView();
        }
        protected virtual void InitializeController()
        {
            if (m_controller != null) return;
            m_controller = Controller.GetInstance(MultitonKey);
        }
        protected virtual void InitializeModel()
        {
            if (m_model != null) return;
            m_model = Model.GetInstance(MultitonKey);
        }
        protected virtual void InitializeView()
        {
            if (m_view != null) return;
            m_view = View.GetInstance(MultitonKey);
        }

        #endregion

        #region Members

        /// <summary>
        /// Private reference to the Controller
        /// </summary>
        protected IController m_controller;

        /// <summary>
        /// Private reference to the Model
        /// </summary>
        protected IModel m_model;

        /// <summary>
        /// Private reference to the View
        /// </summary>
        protected IView m_view;

        /// <summary>
        /// Facade lookup table.
        /// </summary>
        protected static readonly IDictionary<string, IFacade> m_instanceMap;

        /// <summary>
        /// Default name of facade
        /// </summary>
        public const string DEFAULT_KEY = "PureMVC";

        /// <summary>
        /// Exception string for duplicate facade
        /// </summary>
        protected new const string MULTITON_MSG = "Facade instance for this Multiton key already constructed!";

        #endregion
    }
}
